#include<iostream>
#include<cstdio>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<cstring>
#include<sys/wait.h>



//上一次执行的程序的退出码
int lastcode=0;

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# " 

//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc=0;

//2.环境变量表
#define MAX_ENVS 200
char* g_env[MAX_ENVS];
int g_envs=0;

//使用prev_pwd存储最近一次的路径
std::string prev_pwd;

//获取当前登录的用户名
const char* GetUserName()
{
    const char* name=getenv("USER");
    return name==NULL?"None":name;
}

//获取当前主机名
const char* GetHostName()
{
    const char* hostname=getenv("HOSTNAME");
    return hostname==NULL?"None":hostname;

}

//获取当前路径
const char* GetPwd()
{
    static char* cur_pwd=nullptr;
    if(cur_pwd!=NULL)
    {
        free(cur_pwd);
    }
    cur_pwd=getcwd(NULL,0);

    return cur_pwd==NULL? "None":cur_pwd;
}




//得到当前用户的家目录
const char* GetHome()
{
    const char* home=getenv("HOME");
    return home==NULL?"":home;
}


//导入父进程的环境变量表至当前进程当中
void  InitEnv()
{
    extern char** environ;
    memset(g_env,0,sizeof(g_env));
    g_envs=0;
    //获取环境变量
    for(int i=0;environ[i];i++)
    {
        g_env[i]=(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs]=NULL;
    //导入环境变量
    for(int i=0;g_env[i];i++)
    {
        putenv(g_env[i]);
    }
    environ=g_env;
            
}

//执行cd指令
bool cd()
{
    //判断执行的是否是cd -,是的话就获取当前的当前的路径
    if(!(g_argc==2 && (strcmp(g_argv[1],"-")==0)))
    {
        //获取当前路径
        prev_pwd=GetPwd();
    }

    //判断命令行参数的个数
    if(g_argc==1)
    {
        //命令行参数的个数为1就说明用户输出的命令是cd，此时就直接返回当前用户的家目录即可
        std::string home=GetHome();
        if(home.empty())return true;
        chdir(home.c_str());
    }
    else
    {
        //使用变量where得到第二个参数
        std::string where=g_argv[1];
        //当第二个参数为-时执行的命令是返回最近一次的路径
        if(where=="-")
        {
            std::string tmp=GetPwd();
            std::cout<<prev_pwd<<std::endl;
            chdir(prev_pwd.c_str());
            prev_pwd=tmp;
        }
        //当第二个参数是~时执行的命令是返回当前用户的家目录
        else if(where=="~")
        {
            std::string home=GetHome();
            //std::string homestr=home.substr(0);
           // std::cout<<home<<std::endl;
            chdir(home.c_str());
        }
        //不是以上的情况就将路径切换为用户指定的路径
        else{
            chdir(where.c_str());
        }
    }

//更新环境变量表
    int pwd_idx = -1;
    for(int i = 0; g_env[i] != NULL; i++)
    {
        if (strncmp(g_env[i], "PWD=", 4) == 0)
        {
            pwd_idx = i;
            break;
        }
    }

    // 获取当前工作目录
    char* cwd = getcwd(NULL, 0);
    if (cwd == NULL)
    {
        return "None"; // 错误处理
    }

    // 构建新的 PWD 环境变量字符串
    size_t len = strlen(cwd) + 5; // "PWD=" + 字符串长度 + 1
    char* pwd_str =(char*) malloc(len);
    if (pwd_str == NULL)
    {
        free(cwd);
        return "None"; // 内存分配失败
    }
    snprintf(pwd_str, len, "PWD=%s", cwd);
    free(cwd);

    // 更新环境变量表
    if (pwd_idx != -1)
    {
        free(g_env[pwd_idx]); // 释放旧的 PWD 条目
        //printf("%s\n",prev_pwd);
        g_env[pwd_idx] = pwd_str; // 设置新的 PWD 条目
    } else
    {
        // 确保有空间添加新环境变量
        if (g_envs < MAX_ENVS - 1)
        {
            g_env[g_envs++] = pwd_str;
            g_env[g_envs] = NULL; // 确保列表以 NULL 结尾
        } 
    }

    return true;
}

//执行echo指令
void Echo()
{
    //判断用户输入的参数个数是否为2
    if(g_argc==2)
    {
        //将用户输入的第二个参数存储到opt当中
        std::string opt=g_argv[1];
        if(opt=="$?")
        {
            //输出错误码lastcode的值
            std::cout<<lastcode<<std::endl;
            lastcode=0;
        }
        else if(opt[0]=='$')
        {
            //输出对应环境变量的数据
            std::string env_name=opt.substr(1);
            const char*env_vlue=getenv(env_name.c_str());
            if(env_vlue)
                std::cout<<env_vlue<<std::endl;
        }
        else{
            //不为以上的情况就直接将用户输入的第二个参数输出
            std::cout<<opt<<std::endl;

        }

    }
}


//执行export指令
bool Export()
{
     char* newenv =(char*)malloc(strlen(g_argv[1])+1);
    strcpy(newenv,g_argv[1]);

    //std:: cout<<g_argv[1]<<std::endl;
   // std:: cout<<newenv<<std::endl;
    g_env[g_envs++]=newenv;
    g_env[g_envs]=NULL;
    return true;

}


//将得到的当前路径当中得到最后的文件名
std::string DirName(const char* pwd)
{
    //得到当前路径以/之后的文件名
#define SLASH "/"
    std::string dir=pwd;
    if(dir==SLASH)return SLASH;
    auto pos=dir.rfind(SLASH);
    //当pos的返回值为npos时此时出现bug
    if(pos==std::string::npos)return "BUG";
    return dir.substr(pos+1);
}

//构建命令行提示符的输出格式
void MakeCommandLine( char cmd_prompt[],int size )
{
    snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
}


//输出命令行提示符
void  PrintCommandPrompt()
{

    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    printf("%s",prompt);
    fflush(stdout);
}

//从标准输入流当中获取用户输入的内容
bool GetCommandLine(char* out,int size)
{
    char* c=fgets(out,size,stdin);
    if(c==NULL)return false;
    out[strlen(out)-1]=0;
    if(strlen(out)==0)return false;
    return true;

}

//进行对用户输入的命令进行解析
bool CommandParse(char *commandline)
{
#define SEP " "
    g_argc=0;
    //使用strtok进行参数的分割，之后再将参数存储至命令行参数表当中
    g_argv[g_argc++]=strtok(commandline,SEP);
    while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
    //最终统计的参数个数会比实际的多一个，此时需要将g_argc减一
    g_argc--;
    //最后通过判断g_argc的个数来判断用户是否输入有效的命令
    return g_argc>0? true:false;
}



void Print()
{
    for(int i=0;g_argv[i];i++)
    {
        printf("argv[%d]->%s\n",i,g_argv[i]);
    }
    printf("argv:%d\n",g_argc);


    for(int i=0;g_env[i];i++)
    {

        printf("argv[%d]->%s\n",i,g_env[i]);
    }
}


//检查用户输入的指令是否为内建命令
 bool  CheckAndExecBuiltin()
{
    std::string cmd=g_argv[0];
    if(cmd=="cd")
    {
       cd(); 
    return true;
    }
    else if(cmd=="echo")
    {

        Echo();
    return true;

    }
    else if(cmd=="export")
    {
    Export();
    return true;

    }         
    else{
        //……
    }
return false;
}



//创建子进程执行命令
int  Execute()
{
    //创建子进程
    pid_t pid=fork();
    if(pid==0)
    {
        //在子进程当中进行进程替换
        execvp(g_argv[0],g_argv);
        exit(1);
    }
    //进行进程等待
    int status=0;
    pid_t rid=waitpid(pid,&status,0);
    if(rid>0)
    {
        //设置进程退出码lastcode值
        lastcode=WEXITSTATUS(status);
    }


    return 0;

}









int main()
{

    //从父进程当中获取环境变量表
    InitEnv();
    while(true)
    {

        //1.输出命令行提示符
        PrintCommandPrompt();


        //2.获取用户输入的命令
            //创建数组存储用户输出的数据
        char commandline[COMMAND_SIZE];
       if(!GetCommandLine(commandline,sizeof(commandline)))
       continue;

        //3.命令行分析
        if(!CommandParse(commandline))
        continue;
       // Print();
       //4.检测并处理内建命令
       if(CheckAndExecBuiltin())
           continue;
       

        //5.执行命令
        Execute();

       // Print();
    //char arr[1024];
    //scanf("%s",arr);
    }



    return 0;
}
